goog.provide('mp.graphics');
goog.provide('mp.graphics.Graphics');
//goog.exportSymbol('mp.graphics.Graphics', mp.graphics.Graphics);

goog.require('goog.graphics');
goog.require('goog.graphics.ext');
goog.require('mp.graphics.Element');
goog.require('goog.positioning');


/**
 * @constructor
 * @extends {goog.graphics.ext.Graphics}
 */
mp.graphics.Graphics = function(width, height, opt_coordWidth, 
    opt_coordHeight, opt_domHelper, opt_isSimple) {
  goog.base(this, width, height, opt_coordWidth,
      opt_coordHeight, opt_domHelper, opt_isSimple);
  this.createDomInternal_();
};
goog.inherits(mp.graphics.Graphics, goog.graphics.ext.Graphics);


/**
 * @type {Array.<goog.graphics.ext.Group>}
 * @private
 */
mp.graphics.Graphics.prototype.mainLayer_;


/**
 * @type {Array.<goog.graphics.ext.Group>}
 * @private
 */
mp.graphics.Graphics.prototype.captionLayer_;


/**
 * @type {Array.<goog.graphics.ext.Group>}
 * @private
 */
mp.graphics.Graphics.prototype.placeholderLayer_;


/**
 * @type {Array.<goog.graphics.ext.Group>}
 * @private
 */
mp.graphics.Graphics.prototype.bgLayer_;


/**
 * @type {Array.<goog.graphics.ext.Group>}
 * @private
 */
mp.graphics.Graphics.prototype.toolsLayer_;


/**
 * @type {Array.<goog.graphics.ext.Group>}
 * @private
 */
mp.graphics.Graphics.prototype.selectionTool_;


/**
 * @private
 */
mp.graphics.Graphics.prototype.createDomInternal_ = function() {
  this.bgLayer_ = new goog.graphics.ext.Group(this);
  this.placeholderLayer_ = new goog.graphics.ext.Group(this);
  this.mainLayer_ = new goog.graphics.ext.Group(this);
  this.captionLayer_ = new goog.graphics.ext.Group(this);
  this.toolsLayer_ = new goog.graphics.ext.Group(this);

  if (this.getImplementation() instanceof goog.graphics.SvgGraphics) {
    this.createSelectionToolDom_();
  }
};

mp.graphics.Graphics.prototype.getSvgElement = function() {
  return this.getImplementation().getElement();
}

mp.graphics.Graphics.prototype.getGElement = function() {
  return this.getWrapper().getElement();
}

mp.graphics.Graphics.prototype.getParentElement = function() {
  var target = this.getSvgElement();
  var parent = goog.dom.getParentElement(target);
  return !! parent ? parent : target.parentNode;
}

/**
 * @private
 */
mp.graphics.Graphics.prototype.createSelectionToolDom_ = function() {
  var fill = new goog.graphics.SolidFill('green'),
      stroke = new goog.graphics.Stroke(1, 'black');

  var selGrp = new goog.graphics.ext.Group(this.toolsLayer_);
  var selRect = new goog.graphics.ext.Rectangle(selGrp);
  selRect.setStroke(stroke);

  var holders = {
    'tl': new goog.graphics.ext.Ellipse(selGrp),
    'br': new goog.graphics.ext.Ellipse(selGrp)
  };

  for (var idx in holders) {
    holders[idx].setFill(fill);
    holders[idx].setStroke(stroke);
    holders[idx].setSize(10, 10);
  }

  this.selectionTool_ = {
    'sel': selGrp,
    'rect': selRect,
    'holders': holders
  };

  goog.style.showElement(selGrp.getWrapper().getElement(), false);
};


/**
 * @param {mp.graphics.Element} object Graphic element.
 */
mp.graphics.Graphics.prototype.setSelectionTool = function(object) {
  var tool = this.selectionTool_;
  var rect = object.getMainObjectRect();

  tool.sel.setPosition(
      object.getLeft() + rect.left,
      object.getTop() + rect.top, false);
  tool.rect.setSize(rect.width, rect.height, false);

  //goog.style.showElement(tool.sel.getWrapper().getElement(), true);
};

mp.graphics.Graphics.prototype.bringToFront = function(object) {
  goog.dom.appendChild(this.mainLayer_.getWrapper().getElement(), 
      object.getWrapper().getElement());
  goog.dom.appendChild(this.captionLayer_.getWrapper().getElement(), 
      object.captionGroup_.getWrapper().getElement());
  goog.dom.appendChild(this.placeholderLayer_.getWrapper().getElement(), 
      object.placeholderGroup_.getWrapper().getElement());
  goog.dom.appendChild(this.bgLayer_.getWrapper().getElement(), 
      object.bgGroup_.getWrapper().getElement());
};

mp.graphics.Graphics.prototype.sendToBack = function(object) {
  goog.dom.insertChildAt(this.mainLayer_.getWrapper().getElement(), 
      object.getWrapper().getElement(), 0);
  goog.dom.insertChildAt(this.captionLayer_.getWrapper().getElement(), 
      object.captionGroup_.getWrapper().getElement(), 0);
  goog.dom.insertChildAt(this.placeholderLayer_.getWrapper().getElement(), 
      object.placeholderGroup_.getWrapper().getElement(), 0);
  goog.dom.insertChildAt(this.bgLayer_.getWrapper().getElement(), 
      object.bgGroup_.getWrapper().getElement(), 0);
};

mp.graphics.Graphics.prototype.getClientRatio = function() {
  var size = this.getPixelSize();
  return size.width / size.height;
};

mp.graphics.Graphics.prototype.getClientScale = function() {
  var coordSize = this.getCoordSize();
  var size = this.getPixelSize();

  return size.width / coordSize.width;
};

mp.graphics.Graphics.prototype.setClientScale = function(scale) {
  var size = this.getPixelSize();
  this.setCoordSize(
      Math.round(size.width / scale),
      Math.round(size.height / scale));
};

mp.graphics.Graphics.prototype.getClientBRect = function() {
  var element = this.getGElement();

  var clientRect = element.getBoundingClientRect();
  var size = new goog.math.Size(clientRect.width,
        clientRect.height);

  var wrapperPos = goog.style.getClientPosition(this.getParentElement());

  var rect = new goog.math.Rect(
      clientRect.left - wrapperPos.x,
      clientRect.top - wrapperPos.y,
      size.width,
      size.height);

  return rect;
}

mp.graphics.Graphics.prototype.fitToCanvas = function(opt_margin) {
  var rect = this.getClientBRect();
  var canvSize = this.getPixelSize();
  var margin = !! opt_margin ? opt_margin * 2 : 0;
  var scale = Math.min(
      (canvSize.width-margin) / rect.width, 
      (canvSize.height-margin) / rect.height);
  var stageScale = this.getClientScale();
  this.setClientScale(scale * stageScale);
  this.setClientView('center', 'center', opt_margin);
}

mp.graphics.Graphics.prototype.expandCanvas = function(opt_margin) {
  var rect = this.getClientBRect();
  var stageScale = this.getClientScale();
  var margin = !! opt_margin ? opt_margin * 2 : 0;
  var w = Math.round(rect.width / stageScale) + margin, 
      h = Math.round(rect.height / stageScale) + margin;
  
  this.getWrapper().getGraphics().setElementAttributes(
      this.getSvgElement(), {'width': w, 'height': h});
  this.setPixelSize(w, h);
  this.setCoordSize(w, h);
  this.setClientView('left', 'top', opt_margin);
}

mp.graphics.Graphics.prototype.setClientView = function(opt_align, opt_valign, opt_margin) {
  var rect = this.getClientBRect();
  var canvSize = this.getPixelSize();
  var origin = this.getCoordOrigin();
  var stageScale = this.getClientScale();
  var x = origin.x + rect.left / stageScale,
      y = origin.y + rect.top / stageScale;
  var margin = !! opt_margin ? opt_margin : 0;

  opt_align = !! opt_align ? opt_align : 'left';
  opt_valign = !! opt_valign ? opt_valign : 'top';

  switch(opt_align) {
    case 'center':
      x -= (canvSize.width - rect.width) / 2;
      break;
    case 'right':
      x -= (canvSize.width - rect.width);
      x += margin;
      break;
    default:
      x -= margin;
  }
  
  switch(opt_valign) {
    case 'center':
      y -= (canvSize.height - rect.height) / 2;
      break;
    case 'bottom':
      y -= (canvSize.height - rect.height);
      y += margin;
      break;
    default:
      y -= margin;
  }

  this.setCoordOrigin(x, y);
}

mp.graphics.Graphics.prototype.getTextWidth = function(text, font) {
  var canvas = goog.dom.createDom('canvas');
  if (!!(canvas.getContext && canvas.getContext('2d'))) {
    var ctx = canvas.getContext('2d');
    ctx.font = font.size + 'px ' + font.family;
    if (font.bold) {
      ctx.font = 'bold ' + ctx.font;
    }
    if (font.italic) {
      ctx.font = 'italic ' + ctx.font;
    }
    return ctx.measureText(text).width;
  }
  return 0;
};

mp.graphics.Graphics.prototype.measureText = function(text, font, opt_maxWidth, opt_maxHeight) {
  var width = 0,
      lines = [],
      // SVG baseline is on the glyph's base line. We estimate it as 85% of the
      // font height. This is just a rough estimate, but do not have a better way.
      baseline = Math.round(font.size * 0.85) + 4;

  var words = text.split(' ');
  var countWords = words.length;

  if (!! opt_maxWidth && opt_maxWidth > 0) {
    // process max width
    var line = '';
    for (var n = 0; n < countWords; n++) {
      var testLine = line + words[n] + ' ';
      var testWidth = this.getTextWidth(testLine, font);
      if (testWidth > opt_maxWidth) {
        if (line.length)
          lines.push(line);
        line = words[n] + ' ';
      }
      else {
        width = Math.max(width, testWidth);
        line = testLine;
      }
    }
    width = Math.max(width, this.getTextWidth(line, font));
    lines.push(line);

  } else if (!! opt_maxHeight && opt_maxHeight > 0) {
    // process max height
    var pairsLen = [],
        len = words.length;
    for (var i = 0; i < len; i++) {
      if (words[i].length) {
        lines.push(words[i]);
        if (i > 0)
          pairsLen.push(lines[i - 1].length + lines[i].length + 1);
      }
    }
    // join pairs
    while (lines.length * baseline > opt_maxHeight && lines.length > 1) {
      var min = Math.min.apply(Math, pairsLen);
      for (var i = pairsLen.length - 1; i >= 0; i--) {
        if (pairsLen[i] == min) {
          if (!! pairsLen[i - 1])
            pairsLen[i - 1] += lines[i + 1].length + 1;
          if (!! pairsLen[i + 1])
            pairsLen[i + 1] += lines[i].length + 1;

          lines[i + 1] = lines[i] + ' ' + lines[i + 1];

          goog.array.removeAt(lines, i);
          goog.array.removeAt(pairsLen, i);
          break;
        }
      }
    }

    for (var i = lines.length - 1; i >= 0; i--) {
      width = Math.max(width, this.getTextWidth(lines[i], font));
    }

  } else {
    width = Math.max(width, this.getTextWidth(text, font));
    lines.push(text);
  }

  return {lines: lines, width: width, height: lines.length * baseline, baseline: baseline};
};

